package com.hissy.cn.lock;

import java.util.concurrent.TimeUnit;

import com.hissy.cn.redis.JedisClientFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;

/**
 * Created by zhangyanfei on 2019/1/30.
 *
 * @author zhangyanfei
 * @date 2019/01/30
 */
public class JedisLock {
    private static final Logger logger= LoggerFactory.getLogger(JedisLock.class);
    /**
     * redis 锁命名空间
     */
    private static final String LOCK_NAMESPACE="_LOCK";

    /**
     * 默认锁过期时间
     */
    public static final int DeFault_EXPIRY_TIME_MILLIS=Integer.getInteger("com.hissy.cn.jedis.lock.expiry.millis",60*1000);

    /**
     * 重试等待时间
     */
    private static final int DEFAULT_ACQUIRY_RESOLUTION_MILLIS = Integer.getInteger(
        "com.hissy.cn.retry.wait.millis", 100);
    /**
     * reids锁的key
     */
    private  String lockKeyPath;
    /**
     * key 过期时间
     */
    private int lockExpiryInMillis;
    /**
     * 是否锁住
     */
    private boolean locked=false;

    public JedisLock(String namespace,String lockKey){
        this(namespace,lockKey,DeFault_EXPIRY_TIME_MILLIS);
    }

    public JedisLock(String namespace,String lockKey,int expiryTimeMillis){
        this.lockKeyPath=namespace+"/"+LOCK_NAMESPACE+"/"+lockKey;
        this.lockExpiryInMillis=expiryTimeMillis;
    }

    public boolean tryLock(){
        return tryLock(DeFault_EXPIRY_TIME_MILLIS,TimeUnit.MILLISECONDS);
    }

    public synchronized boolean tryLock(int timeout, TimeUnit unit){
        Jedis jedis=null;
        try{
            jedis= JedisClientFactory.getInstance().getDefaultJedisClient();
            long timeoutMillis=unit.toMillis(timeout);
            while(timeoutMillis>=0){
                String result=jedis.set(lockKeyPath,"1","NX","PX",lockExpiryInMillis);
                if("OK".equalsIgnoreCase(result)){
                    locked=true;
                    break;
                }
                // 超时时间递减
                timeoutMillis-=DEFAULT_ACQUIRY_RESOLUTION_MILLIS;
                if(timeoutMillis>0){
                    // 阻塞等待下次重试
                    Thread.sleep(DEFAULT_ACQUIRY_RESOLUTION_MILLIS);
                }
            }
        }catch (Exception e){
            logger.error("{} trylock failed!",lockKeyPath,e);
        }finally {
            JedisLock.forceClose(jedis);
        }
        return locked;
    }

    /**
     * 释放jedis链接,内部创建的jedis实例需要触发释放
     */
    private static void forceClose(Jedis jedis) {
        if (jedis != null) {
            try {
                jedis.close();
            } catch (Exception e) {
                logger.error("insure jedis is closed!", e);
            }
        } else {
            logger.info("jedis is closed!");
        }
    }
}
